package demo2;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.Map;

/**
 * 服务器类：模拟三次数据传输场景（正常帧/错误帧/丢失帧）
 * 核心功能：接收客户端数据帧，按场景处理并返回响应，最终拼接接收的完整数据
 */
public class Server {
    private static final int NUM_SCENES = 3;

    // CRC生成多项式
    private static final String CRC_GENERATOR = "1011";

    // 存储最终接收的完整数据
    private static final StringBuffer receivedData = new StringBuffer();

    public static void main(String[] args) throws IOException {
        ServerSocket serverSocket = new ServerSocket(9000);
        System.out.println("服务器启动，监听端口9000...");

        Socket socket = serverSocket.accept();
        System.out.println("客户端已连接");


        try (DataInputStream in = new DataInputStream(socket.getInputStream());
             DataOutputStream out = new DataOutputStream(socket.getOutputStream())) {

            int frameSeq = 1;
            int sceneIndex = 0;
            boolean transferStarted = false;
            Map<Integer, Boolean> receivedFrames = new HashMap<>(); // 记录已接收的帧（防重复）


            while (true) {
                try {
                    String clientData = in.readUTF();

                    if (clientData.equals("$")) {
                        out.writeUTF("$");
                        if (!transferStarted) {
                            transferStarted = true;
                            System.out.println("收到开始信号，准备接收数据");
                            continue;
                        } else {
                            System.out.println("收到结束信号，传输终止");
                            break;
                        }
                    }

                    if (transferStarted && frameSeq <= NUM_SCENES) {
                        // 解析数据帧：格式为"二进制数据,CRC校验码"
                        String[] parts = clientData.split(",");
                        String binary = parts[0];
                        String crcCode = parts[1];
                        // 将二进制转换为原始字符
                        char dataChar = (char) binaryToDecimal(Long.parseLong(binary));
                        System.out.println("收到数据帧：字符='" + dataChar + "', 二进制=" + binary);


                        switch (sceneIndex) {
                            case 0:
                                handleNormalFrame(binary, crcCode, frameSeq, receivedFrames, out, dataChar);
                                frameSeq++;
                                sceneIndex++;
                                break;
                            case 1:
                                boolean isErrorFixed = handleErrorFrame(binary, crcCode, out, dataChar, in);
                                if (isErrorFixed) {
                                    frameSeq++;
                                    sceneIndex++;
                                }
                                break;
                            case 2:
                                boolean isLossFixed = handleFrameLoss(out, binary, crcCode, frameSeq, receivedFrames, dataChar, in);
                                if (isLossFixed) {
                                    frameSeq++;
                                    sceneIndex++;
                                }
                                break;
                        }
                    }
                } catch (Exception e) {
                    System.out.println("处理异常：" + e.getMessage());
                }
            }


            System.out.println("===== 传输结果 =====");
            System.out.println("服务器最终接收数据：" + receivedData.toString());
        } finally {
            serverSocket.close();
        }
    }

    /**
     * 场景1：正常数据帧处理
     * 逻辑：校验CRC通过→存储字符→发送ack确认（告知客户端可发下一帧）
     */
    private static void handleNormalFrame(String binary, String crcCode, int frameSeq,
                                          Map<Integer, Boolean> receivedFrames, DataOutputStream out, char dataChar) throws IOException {
        // 调用CRC工具类校验：客户端发送的校验码是否正确
        String crcResult = CRC.crcCheck(binary, CRC_GENERATOR, crcCode);
        if (crcResult.isEmpty()) { // 校验通过（余数为0，说明数据未篡改）
            System.out.println("服务器：CRC校验通过，数据正确");
            receivedData.append(dataChar); // 核心：将字符存入最终结果
            out.writeUTF("ack" + frameSeq); // 发送带序号的确认帧（如ack1）
            System.out.println("服务器：发送确认帧：ack" + frameSeq);
        }
    }

    /**
     * 场景2：错误帧处理
     * 逻辑：修改校验码模拟错误→发送nak否定→接收客户端重传帧→校验通过后存储
     */
    private static boolean handleErrorFrame(String binary, String crcCode, DataOutputStream out,
                                            char dataChar, DataInputStream in) throws IOException {
        // 模拟数据错误：翻转校验码最后一位（0→1或1→0）
        String modifiedCrc = flipLastBit(crcCode);
        System.out.println("服务器：模拟错误：校验码修改为" + modifiedCrc);
        // 校验修改后的校验码（必然失败）
        String crcResult = CRC.crcCheck(binary, CRC_GENERATOR, modifiedCrc);

        if (!crcResult.isEmpty()) { // 校验失败
            System.out.println("服务器：CRC校验失败，发送否定帧：nak");
            out.writeUTF("nak"); // 发送否定帧，告知客户端重传
        }

        // 接收客户端重传的帧（客户端收到nak后会重新发送正确帧）
        String retransData = in.readUTF();
        String[] retransParts = retransData.split(",");
        String retransBinary = retransParts[0];
        String retransCrc = retransParts[1];
        System.out.println("收到数据帧：字符='" + dataChar + "', 二进制=" + retransBinary);

        // 校验重传帧
        String retransResult = CRC.crcCheck(retransBinary, CRC_GENERATOR, retransCrc);
        if (retransResult.isEmpty()) {
            System.out.println("服务器：CRC校验通过，数据正确");
            receivedData.append(dataChar); // 存储重传的字符
            out.writeUTF("ack2"); // 场景2对应帧序号2
            System.out.println("服务器：发送确认帧：ack2");
            return true; // 重传成功，允许进入下一场景
        }
        return false;
    }

    /**
     * 场景3：丢失帧处理（贴近真实场景）
     * 逻辑：收到帧后不发送任何响应→客户端超时重传→接收重传帧→校验通过后存储
     */
    private static boolean handleFrameLoss(DataOutputStream out, String binary, String crcCode,
                                           int frameSeq, Map<Integer, Boolean> receivedFrames, char dataChar, DataInputStream in) throws IOException {
        System.out.println("服务器：模拟丢失：未收到有效数据（不发送响应）");

        // 关键：不发送任何响应，客户端会因超时（3秒）判定帧丢失并重传
        // 接收客户端超时后重传的帧
        String retransData = in.readUTF();
        String[] retransParts = retransData.split(",");
        String retransBinary = retransParts[0];
        String retransCrc = retransParts[1];
        System.out.println("收到数据帧：字符='" + dataChar + "', 二进制=" + retransBinary);

        // 校验重传帧（应通过）
        String retransResult = CRC.crcCheck(retransBinary, CRC_GENERATOR, retransCrc);
        if (retransResult.isEmpty()) {
            System.out.println("服务器：CRC校验通过，数据正确");
            receivedData.append(dataChar); // 存储重传的字符
            out.writeUTF("ack3"); // 场景3对应帧序号3
            System.out.println("服务器：发送确认帧：ack3");
            return true; // 重传成功，允许进入下一场景
        }
        return false;
    }

    /**
     * 二进制转十进制（辅助方法）
     * 作用：将8位二进制（如01100001）转换为对应的整数（如97），再转为字符（如'a'）
     */
    private static int binaryToDecimal(long binary) {
        int decimal = 0, i = 0;
        while (binary != 0) {
            // 按位计算：第i位的数值×2^i（二进制转十进制公式）
            decimal += (int) ((binary % 10) * Math.pow(2, i++));
            binary /= 10;
        }
        return decimal;
    }

    /**
     * 翻转校验码最后一位（辅助方法）
     * 作用：场景2中模拟数据错误，修改校验码使CRC校验失败
     */
    private static String flipLastBit(String crc) {
        if (crc.isEmpty()) return crc;
        // 翻转最后一位：0→1，1→0
        return crc.substring(0, crc.length() - 1) + (crc.charAt(crc.length() - 1) == '0' ? '1' : '0');
    }
}